<template>
  <div class="text-white">
    <input
      type="file"
      @change="handleFileChange"
      class="mb-4 p-2 border rounded"
      :disabled="isUploading"
    />
    <button
      @click="handleUpload"
      :disabled="!file || isUploading"
      class="px-4 py-2 bg-blue-500 rounded hover:bg-blue-600 disabled:bg-gray-400"
    >
      {{ isUploading ? `上传中...${progress}%` : "上传" }}
    </button>
    <div v-if="isUploading" class="mt-4">
      <div class="w-full bg-gray-200 rounded-full h-2.5">
        <div
          class="bg-blue-600 h-2.5 rounded-full transition-all duration-300"
          :style="{ width: `${progress}%` }"
        ></div>
      </div>
      <div class="text-sm text-gray-400 mt-1">
        已上传 {{ uploadedSize }} / {{ totalSize }}
      </div>
    </div>
    <div v-if="errorMessage" class="mt-2 text-red-500">
      {{ errorMessage }}
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref, computed } from "vue";
import * as Api from "@/api/index";
import SparkMD5 from "spark-md5";

interface Chunk {
  chunk: Blob;
  hash: string;
  index: number;
  progress: number;
  size: number;
}

interface UploadResult {
  success: boolean;
  chunkIndex?: number;
  error?: any;
}

export default defineComponent({
  setup() {
    const file = ref<File | null>(null);
    const progress = ref(0);
    const isUploading = ref(false);
    const errorMessage = ref("");
    const CHUNK_SIZE = 5 * 1024 * 1024; // 5MB分片大小

    // 计算格式化文件大小
    const formatFileSize = (bytes: number): string => {
      if (bytes === 0) return "0 Bytes";
      const k = 1024;
      const sizes = ["Bytes", "KB", "MB", "GB"];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
    };

    // 计算显示的上传大小
    const uploadedSize = computed(() =>
      formatFileSize(file.value ? (file.value.size * progress.value) / 100 : 0)
    );

    // 计算总文件大小
    const totalSize = computed(() =>
      file.value ? formatFileSize(file.value.size) : "0 Bytes"
    );

    // 计算文件哈希（优化版）
    const calculateFileHash = (file: File): Promise<string> => {
      return new Promise((resolve) => {
        const spark = new SparkMD5.ArrayBuffer();
        const reader = new FileReader();
        const chunkSize = 2 * 1024 * 1024; // 2MB chunks
        let offset = 0;

        const loadNextChunk = () => {
          const end = Math.min(offset + chunkSize, file.size);
          reader.readAsArrayBuffer(file.slice(offset, end));
        };

        reader.onload = (e) => {
          spark.append(e.target?.result as ArrayBuffer);
          offset += chunkSize;

          if (offset < file.size) {
            loadNextChunk();
          } else {
            resolve(spark.end());
          }
        };

        reader.onerror = () => resolve("");
        loadNextChunk();
      });
    };

    // 创建文件分片
    const createFileChunks = async (file: File, chunkSize: number): Promise<Chunk[]> => {
      const chunks: Chunk[] = [];
      let offset = 0;
      let index = 0;

      while (offset < file.size) {
        const end = Math.min(offset + chunkSize, file.size);
        const chunk = file.slice(offset, end);

        chunks.push({
          chunk,
          hash: `${file.name}-${index}`,
          index,
          progress: 0,
          size: chunk.size,
        });

        offset += chunkSize;
        index++;
      }

      return chunks;
    };

    // 构建上传FormData
    const buildChunkFormData = (
      chunk: Chunk,
      fileHash: string,
      filename: string,
      totalChunks: number
    ): FormData => {
      const formData = new FormData();
      formData.append("chunk", chunk.chunk, filename);
      formData.append("fileHash", fileHash);
      formData.append("chunkHash", chunk.hash);
      formData.append("chunkIndex", chunk.index.toString());
      formData.append("fileName", filename);
      formData.append("totalChunks", totalChunks.toString());
      formData.append("fileSize", file.value?.size.toString() || "0");
      return formData;
    };

    // 上传单个分片（带重试）
    const uploadSingleChunk = async (
      chunk: Chunk,
      fileHash: string,
      filename: string,
      totalChunks: number,
      onProgress: (progress: number) => void,
      maxRetries = 1
    ): Promise<UploadResult> => {
      let attempt = 0;
      let lastError: any;

      while (attempt < maxRetries) {
        attempt++;
        try {
          const formData = buildChunkFormData(chunk, fileHash, filename, totalChunks);

          await Api.uploadChunk(formData, {
            onUploadProgress: (progressEvent) => {
              if (progressEvent.total) {
                const percent = Math.round(
                  (progressEvent.loaded / progressEvent.total) * 100
                );
                chunk.progress = percent;
                onProgress(percent);
              }
            },
          });

          return { success: true, chunkIndex: chunk.index };
        } catch (error) {
          lastError = error;
          console.error(
            `分片 ${chunk.index} 上传失败 (尝试 ${attempt}/${maxRetries}):`,
            error
          );

          if (attempt < maxRetries) {
            await new Promise((resolve) => setTimeout(resolve, 1000 * attempt)); // 指数退避
          }
        }
      }

      return {
        success: false,
        chunkIndex: chunk.index,
        error: lastError,
      };
    };

    // 并发控制上传
    const uploadChunks = async (
      chunks: Chunk[],
      fileHash: string,
      filename: string,
      maxConcurrent = 3
    ): Promise<UploadResult[]> => {
      const results: UploadResult[] = [];
      let activeUploads = 0;
      let currentIndex = 0;

      const uploadNext = async (): Promise<void> => {
        if (currentIndex >= chunks.length) return;

        const chunk = chunks[currentIndex++];
        activeUploads++;

        try {
          const result = await uploadSingleChunk(
            chunk,
            fileHash,
            filename,
            chunks.length,
            () => updateTotalProgress(chunks)
          );
          console.log("上传单个分片uploadNext", result);

          results.push(result);
        } finally {
          activeUploads--;
          await uploadNext();
        }
      };

      // 启动初始并发任务
      const initialTasks = Math.min(maxConcurrent, chunks.length);
      const taskPromises: Promise<void>[] = [];
      for (let i = 0; i < initialTasks; i++) {
        taskPromises.push(uploadNext());
      }

      await Promise.all(taskPromises);
      console.log("上传结果：", results);
      return results;
    };

    // 更新总进度
    const updateTotalProgress = (chunks: Chunk[]) => {
      const totalSize = chunks.reduce((sum, chunk) => sum + chunk.size, 0);
      const loadedSize = chunks.reduce((sum, chunk) => {
        return sum + (chunk.size * chunk.progress) / 100;
      }, 0);

      progress.value = Math.min(100, Math.round((loadedSize / totalSize) * 100));
    };

    // 文件选择处理
    const handleFileChange = (e: Event) => {
      const target = e.target as HTMLInputElement;
      if (target.files?.length) {
        file.value = target.files[0];
        progress.value = 0;
        errorMessage.value = "";
      }
    };

    // 在组件中添加初始化相关代码
    const initUpload = async (
      file: File
    ): Promise<{ fileHash: string; shouldUpload: boolean; uploadedChunks: number[] }> => {
      console.log("初始化");
      try {
        // 1. 计算文件哈希
        const fileHash = await calculateFileHash(file);
        if (!fileHash) throw new Error("文件哈希计算失败");
        const fileType = file.type;
        const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
        // 2. 调用初始化接口
        const initResponse = await Api.initUpload({
          fileName: file.name,
          fileSize: file.size,
          fileHash,
          fileType,
          totalChunks: totalChunks,
        });
        console.log("初始化结果：", initResponse);
        // 3. 返回初始化结果
        return {
          fileHash,
          shouldUpload: initResponse.shouldUpload,
          uploadedChunks: initResponse.uploadedChunks || [],
        };
      } catch (error) {
        console.error("初始化失败:", error);
        throw error;
      }
    };
    // 上传处理
    const handleUpload = async () => {
      if (!file.value) return;
      console.log("上传文件:", file.value);
      isUploading.value = true;
      progress.value = 0;
      errorMessage.value = "";

      try {
        // 1. 初始化上传
        const { fileHash, shouldUpload, uploadedChunks } = await initUpload(file.value);
        console.log("初始化结果:", { fileHash, shouldUpload, uploadedChunks });
        if (!shouldUpload) {
          alert("文件已存在，无需重复上传");
          return;
        }

        // 2. 创建分片
        const chunks = await createFileChunks(file.value, CHUNK_SIZE);

        // 3. 标记已上传的分片
        if (uploadedChunks && uploadedChunks.length > 0) {
          chunks.forEach((chunk) => {
            if (uploadedChunks.includes(chunk.index)) {
              chunk.progress = 100; // 标记为已上传
            }
          });
          progress.value = Math.round((uploadedChunks.length / chunks.length) * 100);
        }
        console.log("分片信息:", chunks);
        // 4. 上传未完成的分片
        const chunksToUpload = chunks.filter((chunk) => chunk.progress < 100);
        if (chunksToUpload.length > 0) {
          const uploadResults = await uploadChunks(
            chunksToUpload,
            fileHash,
            file.value.name
          );
          console.log("上传结果:", uploadResults);
        }

        // 5. 合并分片
        const mergeResult = await Api.mergeFile({
          fileHash,
          fileName: file.value.name,
          fileSize: file.value.size,
        });
        console.log("合并结果:", mergeResult);
        alert("文件上传成功");
      } catch (error: any) {
        console.error("上传失败:", error);
        errorMessage.value = `上传失败: ${error.message}`;
      } finally {
        isUploading.value = false;
      }
    };

    return {
      file,
      progress,
      isUploading,
      errorMessage,
      uploadedSize,
      totalSize,
      handleFileChange,
      handleUpload,
    };
  },
});
</script>
